#ifndef VALUE_HPP__
#define VALUE_HPP__

/**
 * \file value.hpp
 * \author Mason Smith <masonium@caltech.edu>
 * Contains definitions for the various parts of the Value class,
 * is a variant type for all runtime values
 */

#include <boost/variant.hpp>
#include <boost/smart_ptr.hpp>
#include <boost/variant.hpp>
#include <boost/array.hpp>
#include <iostream>
#include <algorithm>
#include <vector>
#include "exception.hpp"

using boost::variant;
using boost::shared_ptr;
using boost::shared_array;
using boost::static_visitor;
using boost::recursive_wrapper;
using std::vector;
using std::ostream;
using std::cout;
using std::endl;

/** 
 * Should be used at the end of each binary instruction
 * to default throw type errors when the arguments aren't otherwise suitable.
 */
#define BINARY_DEFAULT_TO_TYPE_ERROR(name, error)                       \
  Value operator() (const NullValue& nv1, const NullValue& nv2) const   \
  {                                                                     \
    throw null_value_error(name + string(": uninitialized value used")); \
  }                                                                     \
  template <typename VqVwV>                                             \
  Value operator() (const NullValue& nv, const VqVwV& v) const          \
  {                                                                     \
    throw null_value_error(name + string(": uninitialized value used")); \
  }                                                                     \
  template <typename UqUwU>                                             \
  Value operator() (const UqUwU& v, const NullValue& nv) const          \
  {                                                                     \
    throw null_value_error(name + string(": uninitialized value used")); \
  }                                                                     \
  template <typename UqUwU, typename VqVwV>                             \
  Value operator() (const UqUwU& u, const VqVwV& v) const               \
  { throw type_error(error); }                                          \
  
#define UNARY_DEFAULT_TO_TYPE_ERROR(name, ret, error)                   \
  ret operator() (const NullValue& nv) const                            \
  {                                                                     \
    throw null_value_error(name + string(": uninitialized value used"));\
  }                                                                     \
  template <typename UqUwU>                                             \
  ret operator() (const UqUwU& u) const { throw type_error(error); }

namespace BMD
{
  class List;
  class Block;
  class NullValue { };

  typedef shared_ptr<List> ListRef;
  typedef shared_ptr<Block> BlockRef;

  typedef variant<recursive_wrapper< ListRef >,
                  int, double, string, 
                  recursive_wrapper< BlockRef >,
                  NullValue > ValueBase;

  /** 
   * Represents runtime values in the virtual machine.
   */
  class Value : public ValueBase
  {
  public:
    Value() : ValueBase(NullValue()) { }
    explicit Value(bool b) : ValueBase(b ? 1 : 0) { } 
    explicit Value(int i) : ValueBase(i) { }
    explicit Value(double d) : ValueBase(d) { }
    explicit Value(string s) : ValueBase(s) { }
    Value(ListRef l) : ValueBase(l) { }
    Value(BlockRef b) : ValueBase(b) { }
    
    /**** type-specific access ****/
    int get_int() const;
    double get_float() const;
    string get_string() const;
    ListRef get_list() const;
    ListRef get_nonempty_list() const;

    BlockRef get_block() const;
    BlockRef get_array() const;
    BlockRef get_struct() const;
    BlockRef get_reference() const;

    //! Returns a string representing the value's type
    string get_type() const;

    // returns true if the value is of a certain type
    template <typename T> bool is_type() const;

    //! copy the value
    Value clone() const;

    //! deep-copy the value
    Value deep_clone() const;

    //! return a string representation of the value
    string to_string() const;

    //! equality
    bool operator==(const Value& v) const;

    // parsing
    static Value parse_value(string s);
    static Value parse_numeric_value(string s);

    // print
    void print(ostream& o, bool print_full = false) const;

  protected:
    template <typename T>
    T get(string expected) const;
  };

  ostream& operator <<(ostream& o, const Value& v);


  /**
   * List runtime value
   * cons-based immutable list value
   */
  class List
  {
  public:
    List(const Value& _car, ListRef _cdr) : car(_car), cdr(_cdr) { }

    Value get_car() const { return car; }    
    ListRef get_cdr() const { return cdr; }
    
    void set_car(Value _car) { car = _car; }
    void set_cdr(ListRef _cdr) { cdr = _cdr; }

    unsigned int length();

    // construct a list from a vector of elements
    static ListRef from_vector(const vector<Value>& args);

  private:
    List(const List& l);
    List& operator=(const List& l); 

  protected:
    Value car;
    ListRef cdr;
  };

  /**
   * Block runtime value
   * runtime safe array value
   */
  class Block
  {
  public:
    enum Tag { Array = 0, Struct, Reference };

    Block(vector<Value>& v, Tag tag = Array);
    Block(Value* v, int _size, Tag tag = Array);
    Block(int _size, Tag tag = Array);
    
    Value operator[](int i) const { return get_elem(i); }

    Value get_elem(int i) const;
    void set_elem(int i, const Value& v);

    unsigned int get_size() const { return size; }
    
    Tag get_tag() const { return tag; }

    // default copy construtor and assignment
    // operator are fine    
  protected:
    Block(const Block& b);
    Block& operator=(const Block& b);

    int assert_in_range(int i) const;
    
    shared_array<Value> values;
    unsigned int size;
    Tag tag;
  };
}


#include "value_visitors.hpp"


namespace BMD
{
  /** Returns true iff value is of the specified type */
  template<typename T>
  bool Value::is_type() const
  {
    return boost::apply_visitor(is_type_visitor<T>(), *this);
  }
}

#endif
